package com.dnjn.happiness.oss.config;

import com.dnjn.happiness.common.core.constant.Constants;
import com.dnjn.happiness.common.core.domain.KV;
import com.dnjn.happiness.common.core.utils.StringUtils;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDate;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Component
@RequiredArgsConstructor
public class MinioTemplate {

    @Autowired
    private MinioClient minioClient;


    public Map<String, String> getPresignedPostFormData(String bucketName, String filename) {
        try {
            // 为存储桶创建一个上传策略，过期时间为7天
            PostPolicy postPolicy = new PostPolicy(bucketName, ZonedDateTime.now().plusMinutes(10));
            // 设置一个参数key，值为上传对象的名称
            postPolicy.addEqualsCondition("key", filename);
            // 添加Content-Type以"image/"开头，表示只能上传照片
            //policy.addStartsWithCondition("Content-Type", "image/");
            // 设置上传文件的大小 64kiB to 10MiB.
            //policy.addContentLengthRangeCondition(12 * 1024, 10 * 1024 * 1024);

            Map<String, String> map = minioClient.getPresignedPostFormData(postPolicy);
            HashMap<String, String> map1 = new HashMap<>();
            map.forEach((k, v) -> {
                map1.put(k.replaceAll("-", ""), v);
            });

            return map1;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    public String getPresignedUrl(String bucketName, String filename, Method method) {
        try {
            Map<String, String> params = KV.createSKV("response-content-type", "application/json");
            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(method)
                    .bucket(bucketName)
                    .object(filename)
                    .expiry(2, TimeUnit.HOURS)
                    .extraQueryParams(params)
                    .build());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 判断bucket是否存在，不存在则创建
     */
    public void existBucket(String bucketName, boolean makeIfNotExist) {
        try {
            boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (makeIfNotExist && (!exists)) {
                makeBucket(bucketName);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 创建存储bucket
     */
    public Boolean makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            return true;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return false;
    }

    /**
     * 删除存储bucket
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
            return true;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return false;
    }

    /**
     * putObject
     */
    public String putObject(String bucketName, String filename, MultipartFile file) {
        try (InputStream in = file.getInputStream()) {
            ObjectWriteResponse resp = putObject(bucketName, filename, file.getContentType(), in);

            return StringUtils.format("/{}/{}", bucketName, filename);//todo
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new RuntimeException(e);
        }
    }

    public ObjectWriteResponse putObject(String bucketName, String filename, String contentType, InputStream in) throws ErrorResponseException, InsufficientDataException, InternalException, InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, ServerException, XmlParserException {
        return minioClient.putObject(PutObjectArgs.builder()
                .bucket(bucketName)
                .object(filename)
                .stream(in, in.available(), -1)
                .contentType(contentType)
                .build());
    }

    /**
     * 上传文件
     */
    public List<String> upload(String bucketName, String prefix, MultipartFile[] multipartFiles) {
        List<String> names = new ArrayList<>(multipartFiles.length);
        for (MultipartFile file : multipartFiles) {
            String filename = file.getOriginalFilename();
            String suffix = filename.substring(filename.lastIndexOf("."));
            String fname = UUID.randomUUID().toString().replaceAll("-", "");
//            filename = filename.replace(suffix, fname + suffix);
            filename = fname + suffix;
            if (StringUtils.isNotBlank(prefix)) {
                filename = StringUtils.format("{}/{}/{}", prefix, LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")), filename);
            }
            putObject(bucketName, filename, file);
            names.add(filename);
        }
        return names;
    }


    /**
     * 下载文件
     */
    public ResponseEntity<byte[]> download(String bucketName, String filename) {
        ResponseEntity<byte[]> responseEntity = null;
        InputStream in = null;
        ByteArrayOutputStream out = null;

        try {
            in = minioClient.getObject(GetObjectArgs.builder()
                    .bucket(bucketName)
                    .object(filename)
                    .build());
            out = new ByteArrayOutputStream();
            IOUtils.copy(in, out);
            byte[] bytes = out.toByteArray();
            HttpHeaders headers = new HttpHeaders();
            try {
                headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, Constants.UTF8));
            } catch (UnsupportedEncodingException e) {
                log.error(e.getMessage(), e);
            }
            headers.setContentLength(bytes.length);
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setAccessControlExposeHeaders(Arrays.asList("*"));
            responseEntity = new ResponseEntity<byte[]>(bytes, headers, HttpStatus.OK);

        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            try {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        log.error(e.getMessage());
                    }
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                log.error(e.getMessage());
            }
        }
        return responseEntity;


    }

    /**
     * 查看文件对象
     */
    public List<ObjectItem> listObjects(String bucketName, String prefix) {
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName)
                        .prefix(prefix)
                        .build());

        List<ObjectItem> objectItems = new ArrayList<>();
        try {
            for (Result<Item> result : results) {
                Item item = result.get();
                ObjectItem objectItem = new ObjectItem();
                objectItem.setObjectName(item.objectName());
                objectItem.setSize(item.size());
                objectItems.add(objectItem);
            }
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new RuntimeException(e);
        }
        return objectItems;
    }

    /**
     * 删除
     *
     * @param fileName
     * @return
     * @throws Exception
     */
    public boolean remove(String bucketName, String fileName) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     * 批量删除文件对象
     *
     * @param bucketName 存储bucket名称
     * @param objects    对象名称集合
     */
    public Iterable<Result<DeleteError>> removeObjects(String bucketName, List<String> objects) {
        List<DeleteObject> dos = objects.stream().map(e -> new DeleteObject(e)).collect(Collectors.toList());
        Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(dos).build());
        return results;
    }


    public static void main(String[] args) {
        System.out.println(defaultBucketPolicy("assets"));
    }

    private static StringBuilder defaultBucketPolicy(String bucketName) {
        StringBuilder builder = new StringBuilder();
        builder.append("{\n" +
                "  \"Version\": \"2012-10-17\",\n" +
                "  \"Statement\": [\n" +
                "    {\n" +
                "      \"Effect\": \"Allow\",\n" +
                "      \"Action\": [\n" +
                "                \"s3:ListAllMyBuckets\",\n" +
                "                \"s3:ListBucket\",\n" +
                "                \"s3:GetBucketLocation\",\n" +
                "                \"s3:GetObject\",\n" +
                "                \"s3:PutObject\",\n" +
                "                \"s3:DeleteObject\"\n" +
                "      ],\n" +
                "      \"Principal\":\"*\",\n" +
                "      \"Resource\": [\n" +
                "        \"arn:aws:s3:::" + bucketName + "/*\"\n" +
                "      ]\n" +
                "    }\n" +
                "  ]\n" +
                "}");
        return builder;
    }

}
